//-------------------------------------------------------------------
//	Copyright (c) 2013-2014  Zhirnov Andrey
//	This file is part of the "UX OpwnGL Framework" project.
//	See copyright notice in "UX_GL_Framework.h".
//-------------------------------------------------------------------
/*
	TODO:
		- optimizations:
			- remove duplicate Flush calls
		- triangulation:
			- fast converting to height map (in GPU)
			- 
*/

#pragma once

#include <Common.h>
#include "Voxel/VoxelTerrainSimulation.h"


class CSampleApplication : public CApplication
{
// types
private:

	struct Voxel
	{
	// types
	public:
		enum {
			TYPE_ID	= 1
		};

	// variables
	public:
		fvec4	data0;
		fvec4	data1;

	// methods
	public:
		static Voxel GetBorderVoxelZPositive ()
		{
			return Voxel();
		}
		
		static Voxel GetBorderVoxelZNegative ()
		{
			return Voxel();
		}
		
		static Voxel GetBorderVoxelXY ()
		{
			return Voxel();
		}
	};

	typedef Voxels::VoxelTerrainSimulation< Voxel, Voxel::TYPE_ID >		TerrainSimulation_t;
	typedef TerrainSimulation_t::ComputePass_t							ComputePass;


	//
	// Simulation Event Listener
	//
	class SimulationEventListener : public TerrainSimulation_t::ISimulationEventListener
	{
	private:
		CSampleApplication *	_app;

	public:
		SimulationEventListener (CSampleApplication *app) : _app(app) {}

		bool OnCreate ()														override;
		void BeforeRunProgram (CLQueue *, CLKernel *, const ivec3 &)			override;
		void AfterRunProgram (CLQueue *, CLImage *, CLImage *, const ivec3 &)	override;
		void HeightMapUpdated (const GLTexture2DPtr &)							override;
	};

	SHARED_POINTER( SimulationEventListener );
	
	
	struct TerrainUB
	{
		fvec3	vLightDir;
		float	fGridScale;

		float	fMaxTessLevel;
		float	fHeightScale;
		float	fDetailLevel;
		float	__align[1];
		///
		TerrainUB () :
			vLightDir(),
			fGridScale(10.0f), fMaxTessLevel(12.0f),
			fHeightScale(-5.0f), fDetailLevel(10.0f)
		{
			vLightDir = fvec3(0.0f, 0.0f, 1.0f).Normalized();
		}

		static float	HeightScaleStep ()		{ return 0.5f; }
		static float	DetailLevelStep ()		{ return 1.0f; }
		static float	TessLevelStep ()		{ return 1.0f; }
		static float	GridScaleStep ()		{ return 1.0f; }
	};
	STATIC_ASSERTE( sizeof(TerrainUB) % sizeof(fvec4) == 0 );


	typedef GLUniformBlock< TerrainUB >		terrainUB_t;
	typedef GLUniform< fmat4 >				uniformMat4_t;
	typedef GLUniform< int >				sampler2D_t;

	struct TerrainData
	{
		GLProgramPtr	prog;
		sampler2D_t		unHeightMap;
		terrainUB_t		ub;
		uniformMat4_t	matMVP;
		GLRenderState	rs;
		uint			patchSize;
		///
		TerrainData () : patchSize(0) {}
	};

	static const uint	HEIGHT_MAP_BORDER = 2;


// variables
private:

	// terrain simulation //
	TerrainSimulation_t			simulation;
	SimulationEventListenerPtr	eventListener;


	// terrain rendering //
	static_array< GLTexture2DPtr, 2 >	heightMaps;

	TerrainData			terrainData;

	GLTexture2DPtr		heightMap;
	
	GLMeshPtr			grid;

	camera_f			camera;
	
	bool				cursorIsHidden : 1;
	bool				vsyncEnabled : 1;
	bool				wireframe : 1;


// methods
public:
	CSampleApplication ();
	~CSampleApplication ();

	void Draw () override;
	void Update () override;
	void OnResize () override;
	void OnInit () override;
	void OnFree () override;

	static bool Run ();

private:
	bool StartSimulation (uni_c_string program, uint flags = ComputePass::READ_WRITE,
							uint requireUpdates = uint(-1), bool genHeightMaps = false);
	void StopSimulation ();

	void DrawTerrain ();


	static const char * VoxelTerrainPath ()			{ return "terrain"; }
	static uvec3		VoxelTerrainBlockSize ()	{ return uvec3( 128 ); }
	static uvec3		VoxelTerrainNumBlocks ()	{ return uvec3( 2, 2, 1 ); }
};
